package cn.song.freight.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.song.freight.domain.dto.OperationLogAddDTO;
import cn.song.freight.domain.dto.OperationLogQueryDTO;
import cn.song.freight.domain.entity.OperationLogDO;
import cn.song.freight.domain.result.PageUtils;
import cn.song.freight.domain.vo.OperationLogStatisticsVO;
import cn.song.freight.domain.vo.OperationStatisticsVO;
import cn.song.freight.domain.vo.OperatorStatisticsVO;
import cn.song.freight.mapper.OperationLogMapper;
import cn.song.freight.service.OperationLogService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * 操作日志服务实现类
 * <pre>
 * 该服务实现提供系统的操作日志管理功能，支持操作日志的记录、查询和统计分析，
 * 为系统提供完整的操作审计和追踪功能。
 *
 * 核心功能：
 * - 操作日志的自动记录和手动记录
 * - 支持多条件组合查询和分页查询
 * - 提供操作统计分析功能
 * - 历史日志清理和管理
 *
 * @author SongRenShuo
 * @version 1.0.0
 * @since 2025-08-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, OperationLogDO> implements OperationLogService {

    /**
     * 记录操作日志
     * <pre>
     * 该方法实现操作日志的记录功能，接收操作日志信息并保存到数据库。
     * 通常由AOP切面或手动调用来记录系统操作。
     * 会自动设置操作时间为当前时间。
     *
     * @param addDTO 操作日志添加数据传输对象，包含日志的基本信息
     * @return 记录结果，true表示记录成功，false表示记录失败
     */
    @Override
    public Boolean recordOperationLog(OperationLogAddDTO addDTO) {
        try {
            OperationLogDO operationLogDO = new OperationLogDO();
            BeanUtils.copyProperties(addDTO, operationLogDO);

            // 设置操作时间为当前时间
            operationLogDO.setOperationTime(new Date());
            operationLogDO.setCreateTime(operationLogDO.getOperationTime());

            return this.save(operationLogDO);
        } catch (Exception e) {
            log.error("记录操作日志失败", e);
            return false;
        }
    }

    /**
     * 批量记录操作日志
     * <pre>
     * 该方法实现批量操作日志的记录功能，用于记录一次操作中涉及多个业务实体的情况。
     * 所有日志记录共享相同的链路追踪ID，便于关联查询。
     *
     * @param addDTOList 操作日志添加数据传输对象列表
     * @return 记录结果，true表示全部记录成功，false表示有记录失败
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchRecordOperationLog(List<OperationLogAddDTO> addDTOList) {
        try {
            if (addDTOList == null || addDTOList.isEmpty()) {
                return true;
            }

            List<OperationLogDO> operationLogDOList = addDTOList.stream()
                    .map(addDTO -> {
                        OperationLogDO operationLogDO = new OperationLogDO();
                        BeanUtils.copyProperties(addDTO, operationLogDO);
                        // 设置操作时间为当前时间
                        operationLogDO.setOperationTime(new Date());
                        return operationLogDO;
                    })
                    .collect(java.util.stream.Collectors.toList());

            return this.saveBatch(operationLogDOList);
        } catch (Exception e) {
            log.error("批量记录操作日志失败", e);
            return false;
        }
    }

    /**
     * 查询操作日志列表（分页）
     * <pre>
     * 该方法实现操作日志的条件查询功能，支持按模块类型、操作类型、操作人、
     * 业务ID、时间范围等多条件组合查询，查询结果按操作时间倒序排列。
     *
     * @param queryDTO 操作日志查询数据传输对象，包含查询条件和分页参数
     * @return 分页的操作日志列表
     */
    @Override
    public PageUtils<OperationLogDO> listOperationLogs(OperationLogQueryDTO queryDTO) {
        LambdaQueryWrapper<OperationLogDO> wrapper = getOperationLogQueryWrapper(queryDTO);

        // 执行分页查询
        Page<OperationLogDO> result = queryDTO.getPage(OperationLogDO.class);
        this.page(result, wrapper);

        // 使用 PageUtils 封装分页结果
        return PageUtils.getPageUtils(result);
    }

    /**
     * 根据链路追踪ID查询相关的所有操作日志
     * <pre>
     * 该方法根据链路追踪ID查询同一次操作中的所有日志记录，
     * 用于追踪完整的操作链路和业务流程。
     *
     * @param traceId 链路追踪ID
     * @return 相关的操作日志列表
     */
    @Override
    public List<OperationLogDO> listOperationLogsByTraceId(String traceId) {
        Assert.notBlank(traceId, "链路追踪ID不能为空");

        LambdaQueryWrapper<OperationLogDO> wrapper = Wrappers.<OperationLogDO>lambdaQuery()
                .eq(OperationLogDO::getTraceId, traceId)
                .orderByDesc(OperationLogDO::getOperationTime);

        return this.list(wrapper);
    }

    /**
     * 查询操作统计信息
     * <pre>
     * 该方法查询系统的操作统计信息，包括各模块和操作类型的统计数据，
     * 用于系统监控和运营分析。默认统计最近7天的数据。
     *
     * @return 操作统计信息列表，包含模块类型、操作类型、操作次数等
     */
    @Override
    public List<OperationStatisticsVO> getOperationStatistics() {
        return baseMapper.selectOperationStatistics();
    }

    /**
     * 查询最近的操作日志
     * <pre>
     * 该方法查询系统最近的操作日志，用于首页展示或快速查看最新操作。
     * 按操作时间倒序排列。
     *
     * @param limit 查询数量限制，默认20条
     * @return 最近的操作日志列表
     */
    @Override
    public List<OperationLogDO> getRecentOperations(Integer limit) {
        if (limit == null || limit <= 0) {
            limit = 20;
        }
        return baseMapper.selectRecentOperations(limit);
    }

    /**
     * 根据操作人统计操作次数
     * <pre>
     * 该方法统计指定天数内各操作人的操作次数，
     * 用于分析用户活跃度和操作行为。
     *
     * @param days 统计天数，默认7天
     * @return 操作人统计信息列表
     */
    @Override
    public List<OperatorStatisticsVO> getOperationCountByOperator(Integer days) {
        if (days == null || days <= 0) {
            days = 7;
        }
        return baseMapper.selectOperationCountByOperator(days);
    }

    /**
     * 清理历史操作日志
     * <pre>
     * 该方法清理指定天数之前的历史操作日志，用于控制数据库存储空间。
     * 建议定期执行此操作，保留90-180天的日志数据即可。
     *
     * @param days 保留天数，删除该天数之前的日志
     * @return 清理的日志记录数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer cleanHistoryLogs(Integer days) {
        Assert.notNull(days, "保留天数不能为空");
        Assert.isTrue(days > 0, "保留天数必须大于0");

        // 安全起见，最少保留30天的日志
        if (days < 30) {
            log.warn("保留天数小于30天，为安全起见调整为30天");
            days = 30;
        }

        return baseMapper.deleteHistoryLogs(days);
    }

    /**
     * 根据业务ID查询相关操作日志
     * <pre>
     * 该方法根据业务ID查询相关的操作日志，用于追踪特定业务数据的操作历史。
     * 支持单个业务ID或批量业务ID查询。
     *
     * @param businessId 业务ID，支持单个ID或逗号分隔的多个ID
     * @param moduleType 模块类型，可选参数用于缩小查询范围
     * @return 相关的操作日志列表
     */
    @Override
    public List<OperationLogDO> listOperationLogsByBusinessId(String businessId, String moduleType) {
        Assert.notBlank(businessId, "业务ID不能为空");

        LambdaQueryWrapper<OperationLogDO> wrapper = Wrappers.<OperationLogDO>lambdaQuery()
                .and(qw -> {
                    // 支持精确匹配或包含匹配（用于批量操作的情况）
                    qw.eq(OperationLogDO::getBusinessId, businessId)
                      .or()
                      .like(OperationLogDO::getBusinessId, businessId + ",")
                      .or()
                      .like(OperationLogDO::getBusinessId, "," + businessId + ",")
                      .or()
                      .like(OperationLogDO::getBusinessId, "," + businessId);
                });

        if (StrUtil.isNotBlank(moduleType)) {
            wrapper.eq(OperationLogDO::getModuleType, moduleType);
        }

        wrapper.orderByDesc(OperationLogDO::getOperationTime);

        return this.list(wrapper);
    }

    /**
     * 根据主键ID查询操作日志详情
     * <pre>
     * 该方法根据主键ID查询单条操作日志详情，用于查看具体操作的详细信息。
     * 包括操作前数据、操作后数据、响应结果等。
     *
     * @param id 操作日志主键ID
     * @return 操作日志详情
     */
    @Override
    public OperationLogDO getOperationLogById(Long id) {
        Assert.notNull(id, "操作日志ID不能为空");
        return this.getById(id);
    }

    /**
     * 构建操作日志查询条件
     *
     * @param queryDTO 查询条件
     * @return LambdaQueryWrapper
     */
    private LambdaQueryWrapper<OperationLogDO> getOperationLogQueryWrapper(OperationLogQueryDTO queryDTO) {
        LambdaQueryWrapper<OperationLogDO> wrapper = Wrappers.lambdaQuery();

        // 链路追踪ID
        if (StrUtil.isNotBlank(queryDTO.getTraceId())) {
            wrapper.eq(OperationLogDO::getTraceId, queryDTO.getTraceId());
        }

        // 模块类型
        if (StrUtil.isNotBlank(queryDTO.getModuleType())) {
            wrapper.eq(OperationLogDO::getModuleType, queryDTO.getModuleType());
        }

        // 操作类型
        if (StrUtil.isNotBlank(queryDTO.getOperationType())) {
            wrapper.eq(OperationLogDO::getOperationType, queryDTO.getOperationType());
        }

        // 业务ID
        if (StrUtil.isNotBlank(queryDTO.getBusinessId())) {
            wrapper.like(OperationLogDO::getBusinessId, queryDTO.getBusinessId());
        }

        // 业务名称（模糊查询）
        if (StrUtil.isNotBlank(queryDTO.getBusinessName())) {
            wrapper.like(OperationLogDO::getBusinessName, queryDTO.getBusinessName());
        }

        // 操作人
        if (StrUtil.isNotBlank(queryDTO.getOperator())) {
            wrapper.eq(OperationLogDO::getOperator, queryDTO.getOperator());
        }

        // 请求IP地址
        if (StrUtil.isNotBlank(queryDTO.getRequestIp())) {
            wrapper.eq(OperationLogDO::getRequestIp, queryDTO.getRequestIp());
        }

        // 操作状态
        if (queryDTO.getStatus() != null) {
            wrapper.eq(OperationLogDO::getStatus, queryDTO.getStatus());
        }

        // 请求方法
        if (StrUtil.isNotBlank(queryDTO.getRequestMethod())) {
            wrapper.eq(OperationLogDO::getRequestMethod, queryDTO.getRequestMethod());
        }

        // 操作描述（模糊查询）
        if (StrUtil.isNotBlank(queryDTO.getOperationDescription())) {
            wrapper.like(OperationLogDO::getOperationDescription, queryDTO.getOperationDescription());
        }

        // 时间范围查询
        if (queryDTO.getStartDate() != null) {
            wrapper.ge(OperationLogDO::getOperationTime, queryDTO.getStartDate());
        }
        if (queryDTO.getEndDate() != null) {
            wrapper.le(OperationLogDO::getOperationTime, queryDTO.getEndDate());
        }

        // 按操作时间倒序排列
        wrapper.orderByDesc(OperationLogDO::getOperationTime);

        return wrapper;
    }


    /**
     * 查询详细统计信息
     * <pre>
     * 该方法查询系统的详细统计信息，包括：
     * - 今日操作数：今天的操作数量
     * - 成功操作数：全部成功的操作数量
     * - 失败操作数：全部失败的操作数量
     * - 总操作数：系统全部操作数量
     *
     * @return 详细统计信息
     */
    @Override
    public OperationLogStatisticsVO getDetailedStatistics() {
        return baseMapper.selectDetailedStatistics();
    }
}
